﻿using System;
using UnityEngine;

namespace GameDesigner
{
    [Serializable]
    public class StateController : IStateController
    {
        public StateLayer[] layers;
        public IStateLayer[] Layers { get => layers; set => layers = (StateLayer[])value; }

        [SerializeField] private StateMachineView _view;
        public StateMachineView View { get => _view; set => _view = value; }

        private Transform _transform; //当view不用的时候，需要设置这个transform对象
        public Transform transform
        {
            get => _view == null ? _transform : _view.Parent != null ? _view.Parent : _view.transform;
            set => _transform = value;
        }
        public object Self { get; set; }

        public int selectedLayerIndex;
        public StateLayer CurrLayer => layers[selectedLayerIndex];

        private bool isInitialize;

        public StateLayer AddNewLayer(string name = "New Layer")
        {
            var layer = new StateLayer()
            {
                name = name,
            };
            layer.stateMachine = new StateMachine()
            {
                name = name,
                Layer = layer,
                Controller = this,
            };
            layers ??= new StateLayer[0];
            ArrayHelper.Add(ref layers, layer);
            return layer;
        }

        public void RemoveLayers(int layerIndex)
        {
            if (layers.Length <= 1)
                return;
            ArrayHelper.RemoveAt(ref layers, layerIndex);
        }

        public void Init(StateMachineView view, Func<StateLayer, IAnimationHandler> onNewHandler, params object[] args)
        {
            View = view;
            Init(onNewHandler, args);
        }

        public void Init(Func<StateLayer, IAnimationHandler> onNewHandler, params object[] args)
        {
            if (isInitialize)
                return;
            isInitialize = true;
            for (int i = 0; i < layers.Length; i++)
            {
                var layer = layers[i];
                layer.LayerIndex = i;
                layer.stateMachine.Controller = this;
                layer.stateMachine.Layer = layer;
                layer.stateMachine.Handler = onNewHandler(layer);
                layer.stateMachine.Init(args);
            }
        }

        public void Update()
        {
            for (int i = 0; i < layers.Length; i++)
            {
                var layer = layers[i];
                layer.Execute(StateMachineUpdateMode.Update);
            }
        }

        public void LateUpdate()
        {
            for (int i = 0; i < layers.Length; i++)
            {
                var layer = layers[i];
                if ((layer.stateMachine.UpdateMode & StateMachineUpdateMode.LateUpdate) != 0)
                    layer.Execute(StateMachineUpdateMode.LateUpdate);
            }
        }

        public void FixedUpdate()
        {
            for (int i = 0; i < layers.Length; i++)
            {
                var layer = layers[i];
                if ((layer.stateMachine.UpdateMode & StateMachineUpdateMode.FixedUpdate) != 0)
                    layer.Execute(StateMachineUpdateMode.FixedUpdate);
            }
        }

        public State AddState(string name, params StateBehaviour[] behaviours) => AddState(name, 0, behaviours);

        public State AddState(string name, int layer, params StateBehaviour[] behaviours) => layers[layer].stateMachine.AddState(name, behaviours);

        public State AddState(string name, bool actionSystem, bool animLoop, bool isCrossFade, bool isExitState, int dstStateID, params StateBehaviour[] behaviours)
            => AddState(name, actionSystem, animLoop, isCrossFade, isExitState, dstStateID, 0, behaviours);

        public State AddState(string name, bool actionSystem, bool animLoop, bool isCrossFade, bool isExitState, int dstStateID, int layer, params StateBehaviour[] behaviours)
            => layers[layer].stateMachine.AddState(name, actionSystem, animLoop, isCrossFade, isExitState, dstStateID, behaviours);

        public State AddSubState(string name) => AddSubState(name, 0);

        public State AddSubState(string name, int layer) => layers[layer].stateMachine.AddSubState(name, default);

        /// <summary>
        /// 切换状态
        /// </summary>
        /// <param name="stateId"></param>
        /// <param name="force"></param>
        /// <param name="layer">切换哪层状态, -1是切换所有层状态</param>
        public void ChangeState(int stateId, int actionId = 0, bool force = false, int layer = 0)
        {
            if (layer != -1)
            {
                layers[layer].stateMachine.ChangeState(stateId, actionId, force);
                return;
            }
            for (int i = 0; i < layers.Length; i++)
            {
                layers[i].stateMachine.ChangeState(stateId, actionId, force);
            }
        }

        /// <summary>
        /// 切换子状态，不需要退出当前状态
        /// </summary>
        /// <param name="stateId"></param>
        /// <param name="actionId"></param>
        /// <param name="layer">切换哪层状态, -1是切换所有层状态</param>
        public void ChangeChildState(int stateId, int actionId = 0, int layer = 0)
        {
            if (layer != -1)
            {
                layers[layer].stateMachine.ChangeChildState(stateId, actionId);
                return;
            }
            for (int i = 0; i < layers.Length; i++)
            {
                layers[i].stateMachine.ChangeChildState(stateId, actionId);
            }
        }

        /// <summary>
        /// 切换状态, 如果状态是子状态机, 则可以指定进入子状态机的某个状态
        /// </summary>
        /// <param name="stateId">当前状态机id</param>
        /// <param name="childStateId">子状态机的状态id</param>
        /// <param name="actionId">子状态机的状态动作id</param>
        /// <param name="force">强制进入?</param>
        /// <param name="layer">切换哪层状态, -1是切换所有层状态</param>
        public void ChangeState(int stateId, int childStateId, int actionId = 0, bool force = false, int layer = 0)
        {
            if (layer != -1)
            {
                layers[layer].stateMachine.ChangeState(stateId, childStateId, actionId, force);
                return;
            }
            for (int i = 0; i < layers.Length; i++)
            {
                layers[i].stateMachine.ChangeState(stateId, childStateId, actionId, force);
            }
        }

        public void SetLayerMask(int layer, AvatarMask mask)
        {
            layers[layer].SetLayerMask(mask);
        }
    }
}